/*
	jQuery tagEditor v1.0.21
    Copyright (c) 2014 Simon Steinberger / Pixabay
    GitHub: https://github.com/Pixabay/jQuery-tagEditor
	License: http://www.opensource.org/licenses/mit-license.php
*/

(function ($) {
  // auto grow input (stackoverflow.com/questions/931207)
  $.fn.tagEditorInput = function () {
    var t = " ", e = $(this), n = parseInt(e.css("fontSize")),
        i = $("<span/>").css({
          position: "absolute",
          top: -9999,
          left: -9999,
          width: "auto",
          fontSize: e.css("fontSize"),
          fontFamily: e.css("fontFamily"),
          fontWeight: e.css("fontWeight"),
          letterSpacing: e.css("letterSpacing"),
          whiteSpace: "nowrap"
        }), s = function () {
          if (t !== (t = e.val())) {
            i.text(t);
            var s = i.width() + n;
            20 > s && (s = 20), s != e.width() && e.width(s)
          }
        };
    return i.insertAfter(e), e.bind("keyup keydown focus", s)
  };

  // plugin with val as parameter for public methods
  $.fn.tagEditor = function (options, val, blur) {

    // helper
    function escape(tag) {
      return tag.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g,
          "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
    }

    // build options dictionary with default values
    var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options),
        selector = this;

    // store regex and default delimiter in options for later use
    o.dregex = new RegExp('[' + o.delimiter.replace('-', '\-') + ']', 'g');

    // public methods
    if (typeof options == 'string') {
      // depending on selector, response may contain tag lists of multiple editor instances
      var response = [];
      selector.each(function () {
        // the editor is the next sibling to the hidden, original field
        var el = $(this), o = el.data('options'), ed = el.next('.tag-editor');
        if (options == 'getTags') {
          response.push({field: el[0], editor: ed, tags: ed.data('tags')});
        } else if (options == 'addTag') {
          if (o.maxTags && ed.data('tags').length >= o.maxTags) {
            return false;
          }
          // insert new tag
          $('<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0]
              + '</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>').appendTo(
              ed).find('.tag-editor-tag')
          .html('<input type="text" maxlength="' + o.maxLength + '">').addClass(
              'active').find('input').val(val).blur();
          if (!blur) {
            ed.click();
          } else {
            $('.placeholder', ed).remove();
          }
        } else if (options == 'removeTag') {
          // trigger delete on matching tag, then click editor to create a new tag
          $('.tag-editor-tag', ed).filter(function () {
            return $(this).text() == val;
          }).closest('li').find('.tag-editor-delete').click();
          if (!blur) {
            ed.click();
          }
        } else if (options == 'destroy') {
          el.removeClass('tag-editor-hidden-src').removeData('options').off(
              'focus.tag-editor').next('.tag-editor').remove();
        }
      });
      return options == 'getTags' ? response : this;
    }

    // delete selected tags on backspace, delete, ctrl+x
    if (window.getSelection) {
      $(document).off('keydown.tag-editor').on(
          'keydown.tag-editor', function (e) {
            if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) {
              try {
                var sel = getSelection(),
                    el = document.activeElement.tagName != 'INPUT' ? $(
                        sel.getRangeAt(0).startContainer.parentNode).closest(
                        '.tag-editor') : 0;
              } catch (e) {
                el = 0;
              }
              if (sel.rangeCount > 0 && el && el.length) {
                var tags = [], splits = sel.toString().split(
                    el.prev().data('options').dregex);
                for (i = 0; i < splits.length; i++) {
                  var tag = $.trim(splits[i]);
                  if (tag) {
                    tags.push(tag);
                  }
                }
                $('.tag-editor-tag', el).each(function () {
                  if (~$.inArray($(this).text(), tags)) {
                    $(this).closest(
                        'li').find('.tag-editor-delete').click();
                  }
                });
                return false;
              }
            }
          });
    }

    return selector.each(function () {
      var el = $(this), tag_list = []; // cache current tags

      // create editor (ed) instance
      var ed = $('<ul ' + (o.clickDelete ? 'oncontextmenu="return false;" '
          : '') + 'class="tag-editor"></ul>').insertAfter(el);
      el.addClass('tag-editor-hidden-src') // hide original field
      .data('options', o) // set data on hidden field
      .on('focus.tag-editor', function () {
        ed.click();
      }); // simulate tabindex

      // add dummy item for min-height on empty editor
      ed.append('<li style="width:1px">&nbsp;</li>');

      // markup for new tag
      var new_tag = '<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0]
          + '</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>';

      // helper: update global data
      function set_placeholder() {
        if (o.placeholder && !tag_list.length && !$(
            '.deleted, .placeholder, input', ed).length) {
          ed.append('<li class="placeholder"><div>' + o.placeholder
              + '</div></li>');
        }
      }

      // helper: update global data
      function update_globals(init) {
        var old_tags = tag_list.toString();
        tag_list = $('.tag-editor-tag:not(.deleted)', ed).map(function (i, e) {
          var val = $.trim(
              $(this).hasClass('active') ? $(this).find('input').val() : $(
                  e).text());
          if (val) {
            return val;
          }
        }).get();
        ed.data('tags', tag_list);
        el.val(tag_list.join(o.delimiter[0]));
        // change callback except for plugin init
        if (!init) {
          if (old_tags != tag_list.toString()) {
            o.onChange(el, ed,
                tag_list);
          }
        }
        set_placeholder();
      }

      ed.click(function (e, closest_tag) {
        var d, dist = 99999, loc;

        // do not create tag when user selects tags by text selection
        if (window.getSelection && getSelection() != '') {
          return;
        }

        if (o.maxTags && ed.data('tags').length >= o.maxTags) {
          ed.find('input').blur();
          return false;
        }

        blur_result = true
        $('input:focus', ed).blur();
        if (!blur_result) {
          return false;
        }
        blur_result = true

        // always remove placeholder on click
        $('.placeholder', ed).remove();
        if (closest_tag && closest_tag.length) {
          loc = 'before';
        } else {
          // calculate tag closest to click position
          $('.tag-editor-tag', ed).each(function () {
            var tag = $(this), to = tag.offset(), tag_x = to.left,
                tag_y = to.top;
            if (e.pageY >= tag_y && e.pageY <= tag_y + tag.height()) {
              if (e.pageX < tag_x) {
                loc = 'before', d = tag_x - e.pageX;
              } else {
                loc = 'after', d = e.pageX - tag_x - tag.width();
              }
              if (d < dist) {
                dist = d, closest_tag = tag;
              }
            }
          });
        }

        if (loc == 'before') {
          $(new_tag).insertBefore(closest_tag.closest('li')).find(
              '.tag-editor-tag').click();
        } else if (loc == 'after') {
          $(new_tag).insertAfter(closest_tag.closest('li')).find(
              '.tag-editor-tag').click();
        } else // empty editor
        {
          $(new_tag).appendTo(ed).find('.tag-editor-tag').click();
        }
        return false;
      });

      ed.on('click', '.tag-editor-delete', function (e) {
        // delete icon is hidden when input is visible; place cursor near invisible delete icon on click
        if ($(this).prev().hasClass('active')) {
          $(this).closest('li').find('input').caret(-1);
          return false;
        }

        var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
        if (o.beforeTagDelete(el, ed, tag_list, tag.text())
            === false) {
          return false;
        }
        tag.addClass('deleted').animate({width: 0}, o.animateDelete,
            function () {
              li.remove();
              set_placeholder();
            });
        update_globals();
        return false;
      });

      // delete on right mouse click or ctrl+click
      if (o.clickDelete) {
        ed.on('mousedown', '.tag-editor-tag', function (e) {
          if (e.ctrlKey || e.which > 1) {
            var li = $(this).closest('li'), tag = li.find('.tag-editor-tag');
            if (o.beforeTagDelete(el, ed, tag_list, tag.text())
                === false) {
              return false;
            }
            tag.addClass('deleted').animate({width: 0}, o.animateDelete,
                function () {
                  li.remove();
                  set_placeholder();
                });
            update_globals();
            return false;
          }
        });
      }

      ed.on('click', '.tag-editor-tag', function (e) {
        // delete on right click or ctrl+click -> exit
        if (o.clickDelete && (e.ctrlKey || e.which > 1)) {
          return false;
        }

        if (!$(this).hasClass('active')) {
          var tag = $(this).text();
          // guess cursor position in text input
          var left_percent = Math.abs(($(this).offset().left - e.pageX) / $(
              this).width()), caret_pos = parseInt(tag.length * left_percent),
              input = $(this).html('<input type="text" maxlength="'
                  + o.maxLength + '" value="' + escape(tag) + '">').addClass(
                  'active').find('input');
          input.data('old_tag', tag).tagEditorInput().focus().caret(caret_pos);
          if (o.autocomplete) {
            var aco = $.extend({}, o.autocomplete);
            // extend user provided autocomplete select method
            var ac_select = 'select' in aco ? o.autocomplete.select : '';
            aco.select = function (e, ui) {
              if (ac_select) {
                ac_select(e, ui);
              }
              setTimeout(function () {
                ed.trigger('click',
                    [$('.active', ed).find('input').closest('li').next(
                        'li').find('.tag-editor-tag')]);
              }, 20);
            };
            input.autocomplete(aco);
          }
        }
        return false;
      });

      // helper: split into multiple tags, e.g. after paste
      function split_cleanup(input) {
        var li = input.closest('li'),
            sub_tags = input.val().replace(/ +/, ' ').split(o.dregex),
            old_tag = input.data('old_tag'), old_tags = tag_list.slice(0),
            exceeded = false, cb_val; // copy tag_list
        for (var i = 0; i < sub_tags.length; i++) {
          tag = $.trim(sub_tags[i]).slice(0, o.maxLength);
          if (o.forceLowercase) {
            tag = tag.toLowerCase();
          }
          cb_val = o.beforeTagSave(el, ed, old_tags, old_tag, tag);
          tag = cb_val || tag;
          if (cb_val === false || !tag) {
            continue;
          }
          // remove duplicates
          if (o.removeDuplicates && ~$.inArray(tag, old_tags)) {
            $('.tag-editor-tag', ed).each(function () {
              if ($(this).text() == tag) {
                $(this).closest('li').remove();
              }
            });
          }
          old_tags.push(tag);
          li.before('<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0]
              + '</div><div class="tag-editor-tag">' + escape(tag)
              + '</div><div class="tag-editor-delete"><i></i></div></li>');
          if (o.maxTags && old_tags.length >= o.maxTags) {
            exceeded = true;
            break;
          }
        }
        input.attr('maxlength', o.maxLength).removeData('old_tag').val('')
        if (exceeded) {
          input.blur();
        } else {
          input.focus();
        }
        update_globals();
      }

      ed.on('blur', 'input', function (e) {
        e.stopPropagation();
        var input = $(this), old_tag = input.data('old_tag'), tag = $.trim(
            input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0]));
        if (!tag) {
          if (old_tag && o.beforeTagDelete(el, ed, tag_list, old_tag)
              === false) {
            input.val(old_tag).focus();
            blur_result = false;
            update_globals();
            return;
          }
          try {
            input.closest('li').remove();
          } catch (e) {
          }
          if (old_tag) {
            update_globals();
          }
        }
        else if (tag.indexOf(o.delimiter[0]) >= 0) {
          split_cleanup(input);
          return;
        }
        else if (tag != old_tag) {
          if (o.forceLowercase) {
            tag = tag.toLowerCase();
          }
          cb_val = o.beforeTagSave(el, ed, tag_list, old_tag, tag);
          tag = cb_val || tag;
          if (cb_val === false) {
            if (old_tag) {
              input.val(old_tag).focus();
              blur_result = false;
              update_globals();
              return;
            }
            try {
              input.closest('li').remove();
            } catch (e) {
            }
            if (old_tag) {
              update_globals();
            }
          }
          // remove duplicates
          else if (o.removeDuplicates) {
            $('.tag-editor-tag:not(.active)', ed).each(function () {
              if ($(this).text() == tag) {
                $(this).closest('li').remove();
              }
            });
          }
        }
        input.parent().html(escape(tag)).removeClass('active');
        if (tag != old_tag) {
          update_globals();
        }
        set_placeholder();
      });

      var pasted_content;
      ed.on('paste', 'input', function (e) {
        $(this).removeAttr('maxlength');
        pasted_content = $(this);
        setTimeout(function () {
          split_cleanup(pasted_content);
        }, 30);
      });

      // keypress delimiter
      var inp;
      ed.on('keypress', 'input', function (e) {
        if (o.delimiter.indexOf(String.fromCharCode(e.which)) >= 0) {
          inp = $(this);
          setTimeout(function () {
            split_cleanup(inp);
          }, 20);
        }
      });

      ed.on('keydown', 'input', function (e) {
        var $t = $(this);

        // left/up key + backspace key on empty field
        if ((e.which == 37 || !o.autocomplete && e.which == 38) && !$t.caret()
            || e.which == 8 && !$t.val()) {
          var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
          if (prev_tag.length) {
            prev_tag.click().find('input').caret(-1);
          } else if ($t.val() && !(o.maxTags && ed.data('tags').length
              >= o.maxTags)) {
            $(new_tag).insertBefore($t.closest('li')).find(
                '.tag-editor-tag').click();
          }
          return false;
        }
        // right/down key
        else if ((e.which == 39 || !o.autocomplete && e.which == 40)
            && ($t.caret() == $t.val().length)) {
          var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
          if (next_tag.length) {
            next_tag.click().find('input').caret(0);
          } else if ($t.val()) {
            ed.click();
          }
          return false;
        }
        // tab key
        else if (e.which == 9) {
          // shift+tab
          if (e.shiftKey) {
            var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag');
            if (prev_tag.length) {
              prev_tag.click().find('input').caret(0);
            } else if ($t.val() && !(o.maxTags && ed.data('tags').length
                >= o.maxTags)) {
              $(new_tag).insertBefore($t.closest('li')).find(
                  '.tag-editor-tag').click();
            }// allow tabbing to previous element
            else {
              el.attr('disabled', 'disabled');
              setTimeout(function () {
                el.removeAttr('disabled');
              }, 30);
              return;
            }
            return false;
            // tab
          } else {
            var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
            if (next_tag.length) {
              next_tag.click().find('input').caret(0);
            } else if ($t.val()) {
              ed.click();
            } else {
              return;
            } // allow tabbing to next element
            return false;
          }
        }
        // del key
        else if (e.which == 46 && (!$.trim($t.val()) || ($t.caret()
            == $t.val().length))) {
          var next_tag = $t.closest('li').next('li').find('.tag-editor-tag');
          if (next_tag.length) {
            next_tag.click().find('input').caret(0);
          } else if ($t.val()) {
            ed.click();
          }
          return false;
        }
        // enter key
        else if (e.which == 13) {
          ed.trigger('click',
              [$t.closest('li').next('li').find('.tag-editor-tag')]);

          // trigger blur if maxTags limit is reached
          if (o.maxTags && ed.data('tags').length >= o.maxTags) {
            ed.find(
                'input').blur();
          }

          return false;
        }
        // pos1
        else if (e.which == 36 && !$t.caret()) {
          ed.find(
              '.tag-editor-tag').first().click();
        }// end
        else if (e.which == 35 && $t.caret() == $t.val().length) {
          ed.find(
              '.tag-editor-tag').last().click();
        }// esc
        else if (e.which == 27) {
          $t.val($t.data('old_tag') ? $t.data('old_tag') : '').blur();
          return false;
        }
      });

      // create initial tags
      var tags = o.initialTags.length ? o.initialTags : el.val().split(
          o.dregex);
      for (var i = 0; i < tags.length; i++) {
        if (o.maxTags && i >= o.maxTags) {
          break;
        }
        var tag = $.trim(tags[i].replace(/ +/, ' '));
        if (tag) {
          if (o.forceLowercase) {
            tag = tag.toLowerCase();
          }
          tag_list.push(tag);
          ed.append('<li><div class="tag-editor-spacer">&nbsp;' + o.delimiter[0]
              + '</div><div class="tag-editor-tag">' + escape(tag)
              + '</div><div class="tag-editor-delete"><i></i></div></li>');
        }
      }
      update_globals(true); // true -> no onChange callback

      // init sortable
      if (o.sortable && $.fn.sortable) {
        ed.sortable({
          distance: 5, cancel: '.tag-editor-spacer, input', helper: 'clone',
          update: function () {
            update_globals();
          }
        });
      }
    });
  };

  $.fn.tagEditor.defaults = {
    initialTags: [],
    maxTags: 0,
    maxLength: 50,
    delimiter: ',;',
    placeholder: '',
    forceLowercase: true,
    removeDuplicates: true,
    clickDelete: false,
    animateDelete: 175,
    sortable: true, // jQuery UI sortable
    autocomplete: null, // options dict for jQuery UI autocomplete

    // callbacks
    onChange: function () {
    },
    beforeTagSave: function () {
    },
    beforeTagDelete: function () {
    }
  };
}(jQuery));
